package Concurrency2.Chapter02_InnerLockPrinciple.P02_BiasedLock;

import org.openjdk.jol.info.ClassLayout;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * 主题: 偏向锁的状态查看试验
 * 1, 场景1: 无竞争状态
 * 2, 场景2: 有竞争状态
 * ----------------------------------------------
 * -- 试验1: 在没有竞争的状态下, 单线程下的锁状态变化
 * 在持有锁的线程, 开始持有锁前
 * 在持有锁的线程, 释放锁之后
 * ----------------------------------------------
 * -- 试验2: 在以下两种状态, 查看竞争场景中锁状态变化
 * 在持有锁的线程, 持有锁 --> 释放锁 --> 第二个线程获取锁
 * 持有锁 --> 不释放锁 --> 第二个线程过来获取锁
 * ----------------------------------------------
 */
public class BiasedLock {
    public static void main(String[] args) {
        // 试验1
        singleThreadTest();
        // 试验2.1
        // multiThreadTest1();
        // 试验2.2
        // multiThreadTest2();

    }

    private static void multiThreadTest2() {
        CountDownLatch countDownLatch = new CountDownLatch(1); // 用于控制两个线程的执行顺序.
        // 任务目的: 执行获取锁, 并在同步代码块内外全部打印对象结构.
        Runnable taskMultiThread = new Runnable() {
            @Override
            public void run() {
                String lockObject = ClassLayout.parseInstance(this).toPrintable();
                long threadId = Thread.currentThread().getId();
                System.out.println("多线程 获取锁前: " + threadId + "\n" + lockObject);
                synchronized (this) {
                    lockObject = ClassLayout.parseInstance(this).toPrintable();
                    System.out.println("多线程 获取锁中: " + threadId + "\n" + lockObject);
                    try {
                        countDownLatch.countDown(); //让线程2在线程1睡眠前就启动
                        TimeUnit.SECONDS.sleep(10);
                        System.out.println("多线程 10S后: " + threadId + "\n" + lockObject);

                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                lockObject = ClassLayout.parseInstance(this).toPrintable();
                System.out.println("多线程 释放锁: " + threadId + "\n" + lockObject);
            }
        };
        // 创建线程1: 任务目的让其获取偏向锁, 并执行完成
        Thread t1 = new Thread(taskMultiThread);
        t1.start();
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        // 创建线程2: 任务目的让其同样获取偏向锁, 并执行完成
        Thread t2 = new Thread(taskMultiThread);
        t2.start();
    }

    private static void multiThreadTest1() {

        CountDownLatch countDownLatch = new CountDownLatch(1); // 用于控制两个线程的执行顺序.
        // 任务目的: 执行获取锁, 并在同步代码块内外全部打印对象结构.
        Runnable taskMultiThread = new Runnable() {
            @Override
            public void run() {
                String lockObject = ClassLayout.parseInstance(this).toPrintable();
                long threadId = Thread.currentThread().getId();
                System.out.println("多线程 获取锁前: " + threadId + "\n" + lockObject);
                synchronized (this) {
                    lockObject = ClassLayout.parseInstance(this).toPrintable();
                    System.out.println("多线程 获取锁中: " + threadId + "\n" + lockObject);
                }

                lockObject = ClassLayout.parseInstance(this).toPrintable();
                System.out.println("多线程 释放锁: " + threadId + "\n" + lockObject);
                countDownLatch.countDown();
            }
        };
        // 创建线程1: 任务目的让其获取偏向锁, 并执行完成
        Thread t1 = new Thread(taskMultiThread);
        t1.start();
        try {
            // 等待线程1执行完成, 然后启动线程2
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 创建线程2: 任务目的让其同样获取偏向锁, 并执行完成
        Thread t2 = new Thread(taskMultiThread);
        t2.start();
    }

    private static void singleThreadTest() {
        Runnable taskSingleThread = new Runnable() {
            @Override
            public void run() {
                System.out.println("ThreadID: "+Thread.currentThread().getId());
                String lockObject = ClassLayout.parseInstance(this).toPrintable();
                System.out.println("单线程 获取锁前: \n" + lockObject);
                synchronized (this) {
                    lockObject = ClassLayout.parseInstance(this).toPrintable();
                    System.out.println("单线程 获取锁中: \n" + lockObject);
                }
                lockObject = ClassLayout.parseInstance(this).toPrintable();
                System.out.println("单线程 释放锁: \n" + lockObject);
            }
        };
        new Thread(taskSingleThread).start();
    }
}
